#pragma once

#include <ctime>
#include <queue>
#include <limits>
#include <cfloat>

#include <wrap/io_trimesh/import.h>
#include <wrap/io_trimesh/export.h>

#include <vcg/complex/trimesh/update/topology.h>
#include <vcg/complex/trimesh/update/color.h>
#include <vcg/complex/trimesh/update/quality.h>
#include <vcg/complex/trimesh/allocate.h>
#include <vcg/complex/trimesh/append.h>
#include <vcg/complex/trimesh/create/platonic.h>

//cuda include files
#include <cuda.h>
#include <cuda_runtime_api.h>
#include <driver_functions.h>
#include <vector_functions.h>
#include <channel_descriptor.h>

//LEMON graph files
#include <lemon/list_graph.h>
#include <lemon/dijkstra.h>

#include <Eigen/Dense>
#define EIGEN_YES_I_KNOW_SPARSE_MODULE_IS_NOT_STABLE_YET
#include <Eigen/Sparse>

//AVL data structure + auxiliary data
#include "AVL.h"
#include "auxiliartypes.h"

#include "ASilhouette.h"

#include "core/gui/opengl/scene/scene.h"
#include "core/gui/opengl/glwindow.h"

#include <QPaintDevice>
#include <QPicture>
#include <QSlider>
#include <QSpinBox>
#include <QGLWidget>
#include <QtOpenGL>
#include "colorpicker.h"

#include "modules/laplace/laplace.h"

//#define SAVEIMAGE

#define NOTCLUSTERED -1
#define JOINT -2
#define SIZE_LIMIT 20

/**
 * Motion Segmenter
 **/
class MotionSegmenter : public QWidget
{

	Q_OBJECT

public:
	MotionSegmenter(std::vector<CGMesh*>& meshlist, int active, GLCamera& camera, QWidget* parent = 0);
	
private:
	std::vector<CGMesh*>& _meshlist;		//reference to mesh list
	int _active;							//index of active mesh
	GLCamera& _camera;						//reference to the camera

	//------------- Data stuff
	std::vector< Edge >			  _edgesData;			//Edge structure
        std::vector< EdgeReferences > _edgesRef;                                //Edge structure


	//-------------- GUI stuff
	QualityMeasure _quality;					//select the quality measure

	QLabel* _silhouetteLabelGPU;				//Label associated with silhouettes

	QButtonGroup*  _qualityMeasureButtonGroup;	//radio buttons

	QPushButton* _clusterDataButton;			//cluster button
    QPushButton* _dataCollect;					//collect button
	QSpinBox* _vpSpinBox;						//spin box for the number of viewpoints
	QSpinBox* _ksSpinBox;						//spin box for the number of silhouettes

	std::vector<CGPoint>	 _camPosition;				//camera position in space
	std::vector<ASilhouette*> _silhouetteVector;		//vector of silhouette
	std::vector<bool*>		 values;					//boolean values
	AVLTree<EdgePair> tree;								//AVL tree shared between silhouettes
	int _silhouetteCounter;								//silhouette counter
	std::set< std::pair<int,int> > silhouettePairs;		//pairs of viewpoint/pose

	/**
	 * Initialize edges of the mesh (if needed)
	 **/
	int initEdges( CGMesh& ref );

	/**
	 * Initialize camera position
	 **/
	int initCamPosition( );
	
	/**
	 * Given an edge, return the set of its neighbors on the silhouette
	 **/
	std::vector<int> getSilhouetteNeighbors( Edge _edge, int v );

	/**
	 * Compute edge lengths
	 **/
	void updateEdgeLengths();

	//-------------CUDA stuff
	std::vector<float3*> faceNormals;
	std::vector<float3*> facePoints;
	int2*   edgeFaces;				//no vector, structure is the same underlying allt he meshes
	bool*	silhouetteValue;		//boolean, contains information about being a silhouette edge

	void collectCudaInformations();
	void extractASilhouette(float3* faceNormalsGPU, float3* facePointsGPU, int2* edgeFacesGPU, bool* silhouetteValueGPU, int index, clock_t& silhouette, clock_t& graph);

	//---------------Segmentation Stuff

	lemon::ListGraph meshGraph;		//graph representation of the mesh (OpenMesh, I miss you)
	lemon::ListGraph dualGraph;		//graph representation of the mesh (OpenMesh, I miss you)

	//other stuff, hopefully the last...
	std::vector<QColor> clusterColors;
    std::vector<std::set<int> > clusterList;
	std::set<int> clusterCandidate;
	std::vector<int> vertexToCluster;

	//saliency of the vertices
	Eigen::SparseVector<int>* saliency;
	//distance from salient vertex
	std::vector<float> distanceFromSalient;
	//min and max distance value
	std::pair<float,float> minmax;

	
	//timings measurement
	float extractionTime;
	float analysisTime;
	float clusteringTime;

	//---------------Diffusion distance stuff
	int eval_size;	//size of the eigenpairs
	float *evals;	//eigenvalues
	float *evecs;	//eigenvectors
	bool  *mask;	//boolean mask for selective approach
	
	float *evecsGPU;	//eigenvalues for GPU storing
	bool*  maskGPU;		//boolean mask for GPU storing
	float* outputGPU;	//output from GPU computation
	cudaArray* evals_array;		//array for ieigenvalues

    float *evalsGPU;    //DEBUG

	float* distance;	//distance vector
	int nEvals;			//number of evals used for computation

	std::set<int> boundaryVertices;		//vertices on the boundaries of clusters
	bool firstcluster;					//indicates if we are working with the first cluster
	
	void prepareCUDADistance();	//prepare CUDA for computing diffusion distance
	void clearCUDADistance();	//prepare CUDA for computing diffusion distance

	float ts;	//threshold
	float t;	//scale factor

	void finalizeCluster(int clusterA, int clusterB);
	
	int  getNextSeed(std::vector<float>& distanceFromSalient);
	void getDistanceFromVertex(int vi, int nEvals);
	void firstCluster(Eigen::SparseVector<int>& saliency, int tshold) ;

    void createJoints();
    std::set<int> getNeighborhood(int central, int radius);

    void laplaceOperator(void);
    LaplaceOperator* laplacian;
	
private slots:
	//interface for setting quality measure
	void setQualityMeasure();

	//A.Silhouettes
	void firstStep();
	//analysis
	void secondStep();
	//clustering
	void thirdStep();


	//other stuff
    void storeCluster(QColor current);
	void colorLastCluster(QColor current);
	void finalize();
	//color picker slots
	void undoLastCluster();
	void splitLastCluster();
        void openSegmentation();
        void saveSegmentation();
		void openSegmentationSumner();


	//---------------------------------------
    void selectViewPoints(int n);
	void selectSilhouettes(int n);

signals:
	void updateView();
	void showColorPicker();
    void remakeMesh();

};
